﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartMathLibrary.Space.CoreTypes;

namespace SmartMathLibrary.Space.QueryEngine
{
    /// <summary>
    /// Defines a lower border between option.
    /// </summary>
    public enum LowerBetweenOption
    {
        /// <summary>
        /// Defines the greater than operator.
        /// </summary>
        GreaterThan = 0,
        /// <summary>
        /// Defines the greater than or equal operator.
        /// </summary>
        GreaterThanOrEqual = 1
    }

    /// <summary>
    /// Defines an upper border between option.
    /// </summary>
    public enum UpperBetweenOption
    {
        /// <summary>
        /// Defines the lower than operator.
        /// </summary>
        LowerThan = 0,
        /// <summary>
        /// Defines the lower than or equal operator.
        /// </summary>
        LowerThanOrEqual = 1
    }

    /// <summary>
    /// This class represents an between query for a space container.
    /// </summary>
    [Serializable]
    public class BetweenQuery : AbstractSpaceQuery
    {
        /// <summary>
        /// The lower border value.
        /// </summary>
        private ISpaceEntry lowerBorderValue;

        /// <summary>
        /// The upper border value.
        /// </summary>
        private ISpaceEntry upperBorderValue;

        /// <summary>
        /// The lower between option.
        /// </summary>
        private LowerBetweenOption lowerBetweenOption;

        /// <summary>
        /// The upper between option.
        /// </summary>
        private UpperBetweenOption upperBetweenOption;

        /// <summary>
        /// Initializes a new instance of the <see cref="BetweenQuery"/> class.
        /// </summary>
        /// <param name="lowerBorderValue">The lower border value.</param>
        /// <param name="upperBorderValue">The upper border value.</param>
        public BetweenQuery(ISpaceEntry lowerBorderValue, ISpaceEntry upperBorderValue)
            : base()
        {
            this.lowerBorderValue = lowerBorderValue;
            this.upperBorderValue = upperBorderValue;
            this.lowerBetweenOption = LowerBetweenOption.GreaterThanOrEqual;
            this.upperBetweenOption = UpperBetweenOption.LowerThanOrEqual;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BetweenQuery"/> class.
        /// </summary>
        /// <param name="lowerBorderValue">The lower border value.</param>
        /// <param name="upperBorderValue">The upper border value.</param>
        /// <param name="lowerBetweenOption">The lower between option.</param>
        /// <param name="upperBetweenOption">The upper between option.</param>
        public BetweenQuery(ISpaceEntry lowerBorderValue, ISpaceEntry upperBorderValue,
                            LowerBetweenOption lowerBetweenOption, UpperBetweenOption upperBetweenOption)
            : base()
        {
            this.lowerBorderValue = lowerBorderValue;
            this.upperBorderValue = upperBorderValue;
            this.lowerBetweenOption = lowerBetweenOption;
            this.upperBetweenOption = upperBetweenOption;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BetweenQuery"/> class.
        /// </summary>
        /// <param name="lowerBorderValue">The lower border value.</param>
        /// <param name="upperBorderValue">The upper border value.</param>
        /// <param name="lowerBetweenOption">The lower between option.</param>
        /// <param name="upperBetweenOption">The upper between option.</param>
        /// <param name="status">The object status criteria.</param>
        public BetweenQuery(ISpaceEntry lowerBorderValue, ISpaceEntry upperBorderValue,
                            LowerBetweenOption lowerBetweenOption, UpperBetweenOption upperBetweenOption,
                            SpaceObjectStatus status)
            : base(status)
        {
            this.lowerBorderValue = lowerBorderValue;
            this.upperBorderValue = upperBorderValue;
            this.lowerBetweenOption = lowerBetweenOption;
            this.upperBetweenOption = upperBetweenOption;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BetweenQuery"/> class.
        /// </summary>
        /// <param name="lowerBorderValue">The lower border value.</param>
        /// <param name="upperBorderValue">The upper border value.</param>
        /// <param name="lowerBetweenOption">The lower between option.</param>
        /// <param name="upperBetweenOption">The upper between option.</param>
        /// <param name="limit">The maximum number of query results.</param>
        public BetweenQuery(ISpaceEntry lowerBorderValue, ISpaceEntry upperBorderValue,
                            LowerBetweenOption lowerBetweenOption, UpperBetweenOption upperBetweenOption, int limit)
            : base(limit)
        {
            this.lowerBorderValue = lowerBorderValue;
            this.upperBorderValue = upperBorderValue;
            this.lowerBetweenOption = lowerBetweenOption;
            this.upperBetweenOption = upperBetweenOption;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BetweenQuery"/> class.
        /// </summary>
        /// <param name="lowerBorderValue">The lower border value.</param>
        /// <param name="upperBorderValue">The upper border value.</param>
        /// <param name="lowerBetweenOption">The lower between option.</param>
        /// <param name="upperBetweenOption">The upper between option.</param>
        /// <param name="limit">The maximum number of query results.</param>
        /// <param name="status">The object status criteria.</param>
        public BetweenQuery(ISpaceEntry lowerBorderValue, ISpaceEntry upperBorderValue,
                            LowerBetweenOption lowerBetweenOption, UpperBetweenOption upperBetweenOption, int limit,
                            SpaceObjectStatus status)
            : base(limit, status)
        {
            this.lowerBorderValue = lowerBorderValue;
            this.upperBorderValue = upperBorderValue;
            this.lowerBetweenOption = lowerBetweenOption;
            this.upperBetweenOption = upperBetweenOption;
        }

        /// <summary>
        /// Gets or sets the lower border value.
        /// </summary>
        /// <value>The lower border value.</value>
        public ISpaceEntry LowerBorderValue
        {
            get { return this.lowerBorderValue; }
            set { this.lowerBorderValue = value; }
        }

        /// <summary>
        /// Gets or sets the upper border value.
        /// </summary>
        /// <value>The upper border value.</value>
        public ISpaceEntry UpperBorderValue
        {
            get { return this.upperBorderValue; }
            set { this.upperBorderValue = value; }
        }

        /// <summary>
        /// Gets or sets the lower between option.
        /// </summary>
        /// <value>The lower between option.</value>
        public LowerBetweenOption LowerBetweenOption
        {
            get { return this.lowerBetweenOption; }
            set { this.lowerBetweenOption = value; }
        }

        /// <summary>
        /// Gets or sets the upper between option.
        /// </summary>
        /// <value>The upper between option.</value>
        public UpperBetweenOption UpperBetweenOption
        {
            get { return this.upperBetweenOption; }
            set { this.upperBetweenOption = value; }
        }

        /// <summary>
        /// Defines the query criteria of the specified query.
        /// </summary>
        /// <param name="item">The query source items.</param>
        /// <returns>
        /// The space ID fulfills the query criteria the method returns true otherwise, false.
        /// </returns>
        public override bool QueryDefinition(ISpaceEntry item)
        {
            if ((this.lowerBetweenOption == LowerBetweenOption.GreaterThan) &&
                (this.upperBetweenOption == UpperBetweenOption.LowerThan))
            {
                return item.GreaterThan(this.lowerBorderValue) && item.LowerThan(this.upperBorderValue);
            }
            else if ((this.lowerBetweenOption == LowerBetweenOption.GreaterThanOrEqual) &&
                     (this.upperBetweenOption == UpperBetweenOption.LowerThan))
            {
                return (item.GreaterThan(this.lowerBorderValue) || item.Equals(this.lowerBorderValue)) &&
                       item.LowerThan(this.upperBorderValue);
            }
            else if ((this.lowerBetweenOption == LowerBetweenOption.GreaterThan) &&
                     (this.upperBetweenOption == UpperBetweenOption.LowerThanOrEqual))
            {
                return item.GreaterThan(this.lowerBorderValue) &&
                       (item.LowerThan(this.upperBorderValue) || item.Equals(this.upperBorderValue));
            }
            else
            {
                return (item.GreaterThan(this.lowerBorderValue) || item.Equals(this.lowerBorderValue)) &&
                       (item.LowerThan(this.upperBorderValue) || item.Equals(this.upperBorderValue));
            }
        }
    }
}